package cn.ococci.dm.Main.Core;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

import cn.ococci.dm.Base.BaseApp;
import cn.ococci.dm.Main.Const;
import cn.ococci.dm.Main.OtaType;
import cn.ococci.dm.Main.Tools.FileTool;
import cn.ococci.dm.Main.Tools.LogTool;
import cn.ococci.dm.Main.Tools.ThreadTool;
import cn.ococci_dm.R;


/**
 * Created by cyan on 16/1/22.
 *
 * 升级OTA
 */
public class UpGradeCommand {

    public static final String TAG = UpGradeCommand.class.getSimpleName();

    private final static int BUFF_LEN = 512;

    private static final  int port = 10001;

    private final static String ver_flag_start = "pr_version_start[";
    private final static String ver_flag_end = "]pr_version_end";

    private ServerSocket server = null;
    public UpgradeCompletedListener upgradeCompletedListener;

    private String ip = "";
    private String fileName = "";
    private OtaType type;

    private boolean loop = true;

    public interface UpgradeCompletedListener{

        void onUpgradeError(String errorMsg);

        void onStartListener();

        void onCompletedListener(boolean success,OtaType type);

    }


    public void initUpGradeCommand(OtaType type,String ip,UpgradeCompletedListener upgradeCompletedListener){
        this.type = type;
        this.ip = ip;
        this.upgradeCompletedListener = upgradeCompletedListener;

        fileName = FileTool.getOtaPath()+((type == OtaType.OTC_AP) ? Const.OTA_AP_NAME:Const.OTA_PR_NAME);

        if(!new File(fileName).exists()){
            upgradeCompletedListener.onUpgradeError(BaseApp.findResIdDecoderString(R.string.no_exits_ota));
            return;
        }

        startListenTCP(fileName);
    }


    /**
     * 开始升级OTA操作
     * @param filename
     */
    public void startListenTCP(String filename) {

        fileName = filename;

        ThreadTool.runOnNewThread(new Runnable() {
            @Override
            public void run() {

                try {

                    Socket localClient = new Socket(Const.IP, Const.PORT);
                    DataOutputStream output = new DataOutputStream(localClient.getOutputStream());

                    String upgradeCommand = EncodeCommandUtils.builderUpgradeWithOta(type,ip,port);

                    output.write(upgradeCommand.getBytes());
                    output.flush();
                    output.close();
                    localClient.close();

                    if(server == null){
                        server = new ServerSocket(port);
                        server.setReuseAddress(true);
                    }

                    LogTool.log(TAG,"启动 ServerSocket,等待客户端连接... 需要传输的文件是:" + fileName);
                    while (loop){
                        Socket client = server.accept();
                        upgradeCompletedListener.onStartListener();
                        new Thread(new SSocket(client)).start();
                    }

                } catch (IOException e) {
                    upgradeCompletedListener.onUpgradeError("连接出错");
                    e.printStackTrace();
                } finally {
                    try {
                        if (server != null) {
                            server.close();
                            server = null;
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
    }


    public static String getVersionFromOTABin(File file) throws FileNotFoundException, IOException {
        String s = readFile(file);
        int from_n = s.indexOf(ver_flag_start);
        if (from_n > -1) {
            from_n += ver_flag_start.length();
        } else {
            return "";
        }
        int to_n = s.indexOf(ver_flag_end);
        if (to_n > -1) {
            to_n += ver_flag_end.length();
        } else {
            return "";
        }
        return s.substring(from_n, to_n);
    }

    private static String readFile(File file) throws FileNotFoundException, IOException {
        StringBuffer sb = new StringBuffer();
        FileReader fl = new FileReader(file);
        BufferedReader bf = new BufferedReader(fl);
        String context;
        do {
            context = bf.readLine();
            if (context == null) {
                break;
            } else if (!context.trim().equals("")) {
                sb.append(context + " ");
            }
        } while (context != null);
        bf.close();
        return sb.toString();
    }

    public static String getVersionFromOTABin(String fileName) {
        try {
            Charset charset = Charset.defaultCharset();
            // Charset charset = Charset.forName("GB18030");
            CharsetDecoder decoder = charset.newDecoder();
            FileInputStream fis = new FileInputStream(fileName);
            FileChannel fc = fis.getChannel();

            int sz = (int) fc.size();
            MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);

            CharBuffer cb = decoder.decode(bb);

            String s = String.valueOf(cb);

            int from_n = s.indexOf(ver_flag_start);
            if (from_n > -1) {
                from_n += ver_flag_start.length();
            } else {
                return "";
            }
            int to_n = s.indexOf(ver_flag_end);
            if (to_n > -1) {
                to_n += ver_flag_end.length();
            } else {
                return "";
            }
            fc.close();
            return s.substring(from_n, to_n);
        } catch (Exception e) {
            System.out.println(e);
        }
        return "";
    }

    private static void printByteArr(byte[] in) {
        for (int i = 0; i < in.length; i++) {
            System.out.print(Integer.toHexString(0xFF & in[i]) + " ");
        }
    }



    // 服务器进程
    class SSocket implements Runnable {
        Socket client;

        public SSocket(Socket client) {
            this.client = client;
        }

        public  void run() {
            DataOutputStream output;
            try {
                output = new DataOutputStream(client.getOutputStream());
                SendFile(fileName, getOTABinHead(fileName), output);
                client.close();
                upgradeCompletedListener.onCompletedListener(true,type);
            } catch (IOException e) {
                upgradeCompletedListener.onCompletedListener(false,type);
                e.printStackTrace();
            }
        }
    }

    private static int toUnsignChat(byte bytes) {
        int ret = (0x0FF & bytes);
        return ret;
    }

    private static int sum(byte[] bytes, int len) {
        int ret = 0;
        for (int i = 0; i < len; i++) {
            ret += toUnsignChat(bytes[i]);
        }
        return ret;
    }

    private static int getSumFromArr(int[] file_checksum) {
        int ret = 0;
        for (int i = 0; i < file_checksum.length; i++) {
            ret += file_checksum[i];
        }
        return ret;
    }

    private static int[] byteToArr(byte[] buff, int from, int len) {
        int[] ret = new int[len];
        for (int i = 0; i < len; i++) {
            ret[i] += toUnsignChat(buff[i + from]);
        }
        return ret;
    }

    public static byte[] getOTABinHead(String fileName) {
        RandomAccessFile randomFile = null;
        int checkSum = 0;
        int fileLen = 0;

        int[] file_checksum = { 0, 0, 0, 0 };
        try {
            // 打开一个随机访问文件流，按只读方式
            randomFile = new RandomAccessFile(fileName, "r");
            fileLen = (int) randomFile.length();
            byte[] bytes = new byte[BUFF_LEN];
            int readSize = 0;
            int i = 0;
            while ((readSize = randomFile.read(bytes)) != -1) {
                checkSum += getSumFromArr(file_checksum);
                System.out.println(String.format(
                        "Round[%1$s] size=%7$s checkSum=%2$s file_checksum={%3$s,%4$s, %5$s, %6$s}", i, checkSum,
                        file_checksum[0], file_checksum[1], file_checksum[2], file_checksum[3], readSize));
                checkSum += sum(bytes, readSize - 4);
                file_checksum = byteToArr(bytes, readSize - 4, 4);
                System.out.println(String.format("Round[%1$s] file_checksum={%2$s,%3$s, %4$s, %5$s}", i,
                        file_checksum[0], file_checksum[1], file_checksum[2], file_checksum[3]));
                i++;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
        byte[] checkSumByte = int2Byte(checkSum);
        byte[] fileLenByte = int2Byte(fileLen);
        byte[] ret = { checkSumByte[0], checkSumByte[1], checkSumByte[2], checkSumByte[3], 0, 0, 0, 0, fileLenByte[0],
                fileLenByte[1], fileLenByte[2], fileLenByte[3] };
        return ret;
    }

    public static void SendFile(String fileName, byte[] headData, DataOutputStream output) {
        RandomAccessFile randomFile = null;
        try {
            randomFile = new RandomAccessFile(fileName, "r");
            byte[] bytes = new byte[BUFF_LEN];
            int readSize = 0;
            output.write(headData, 0, 12);
            int i = 0;
            while ((readSize = randomFile.read(bytes)) != -1) {
                output.write(bytes, 0, readSize);
                System.out.println(String.format("Round[%1$s] send size=%2$s", i, readSize));
                i++;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
    }

    private static byte[] int2Byte(int a) {
        byte[] ret = new byte[4];
        for (int i = 0; i < 4; i++) {
            ret[i] = (byte) (0x0FF & (a >> (8 * i)));
        }
        return ret;
    }

    public void breakLoop(){
        this.loop = false;
    }

}
